﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UvsChess;

namespace StudentAI
{
    public class Heuristics
    {
        int pawnValue, rookValue, bishopValue, knightValue, queenValue, kingValue;
        public static int CHECKMATE = 2000000000;
        public static bool IN_CHECKMATE = false;
        public Heuristics(int pValue = 3, int rValue = 15, int bValue = 9, int nValue = 9, int qValue = 27, int kValue = 9999) {
            pawnValue = pValue;
            rookValue = rValue;
            bishopValue = bValue;
            knightValue = nValue;
            queenValue = qValue;
            kingValue = kValue;
        }//End Constructor

        int getPieceValue(ChessPiece piece) { 
            switch(piece){
                case ChessPiece.Empty:
                    return 0;
                case ChessPiece.BlackBishop:
                case ChessPiece.WhiteBishop:
                    return bishopValue;
                case ChessPiece.BlackKing:
                case ChessPiece.WhiteKing:
                    return kingValue;
                case ChessPiece.BlackKnight:
                case ChessPiece.WhiteKnight:
                    return knightValue;
                case ChessPiece.BlackPawn:
                case ChessPiece.WhitePawn:
                    return pawnValue;
                case ChessPiece.BlackQueen:
                case ChessPiece.WhiteQueen:
                    return queenValue;
                case ChessPiece.BlackRook:
                case ChessPiece.WhiteRook:
                    return rookValue;
                default:
                    //Do Nothing, Shouldn't get here
                    break;
            }
            return 0;
        }//End getPieceValue

        public delegate int HeuristicFunction(ChessBoard board, ChessColor color, ChessMove move);

     

        public int basicSumScore(ChessBoard board, ChessColor color, ChessMove greedy)
        {
            int sum = 0;
            List<ChessMove> moves = MoveGeneration.getAllMoves(board, ChessUtils.getOppositeColor(color));
            //moves.RemoveAll(move => MoveValidation.putsKingInCheck(board, move, ChessUtils.getOppositeColor(color)));
            if (moves.Count == 0 && MoveValidation.kingInCheck(board, MoveValidation.findKing(board, ChessUtils.getOppositeColor(color)), ChessUtils.getOppositeColor(color)))
            {
                sum = CHECKMATE;
                IN_CHECKMATE = true;
                return sum;
            }
            for (int y = 0; y < ChessBoard.NumberOfRows; y++)
            {
                for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                {
                    ChessPiece currentPiece = board[x, y];
                    if (MoveValidation.SameColor(currentPiece, color))
                    {
                        if (color == ChessColor.White && currentPiece == ChessPiece.WhitePawn && (y == 0 || y == 7))
                            sum += 8;
                        if (color == ChessColor.Black && currentPiece == ChessPiece.BlackPawn && (y == 0 || y == 7))
                            sum += 8;
                        sum += getPieceValue(currentPiece);
                    }
                    else //Opposite color = minus value 
                    {
                        if (color == ChessColor.White && currentPiece == ChessPiece.WhitePawn
                            && (y == 0 || y == 7))
                            sum -= 8;
                        if (color == ChessColor.Black && currentPiece == ChessPiece.BlackPawn
                            && (y == 0 || y == 7))
                            sum -= 8;
                        sum -= getPieceValue(currentPiece);
                    }
                    
                }  
            }
            //points for pawn promotion
            
            return sum;
        }//End basicSumScore

        public int MikesSumScore(ChessBoard board, ChessColor color, ChessMove move)
        {
            int sum = 0;
            int tx = move.To.X;
            int ty = move.To.Y;
            ChessPiece currPiece;
            currPiece = board[tx, ty];
            //StudentAI.studentAI.Log(board[tx,ty].ToString() + "x = " + move.From.X + "y= " + move.From.Y + "\n");
            //List<ChessMove> moves = MoveGeneration.getAllMoves(board, ChessUtils.getOppositeColor(color));
            List<ChessMove> moves = MoveGeneration.getAllMoves(board, color);
            List<ChessMove> opponentMoves = MoveGeneration.getAllMoves(board, ChessUtils.getOppositeColor(color));
            //moves.RemoveAll(m => MoveValidation.putsKingInCheck(board, m, ChessUtils.getOppositeColor(color)));
            if (opponentMoves.Count == 0 && MoveValidation.kingInCheck(board, MoveValidation.findKing(board, ChessUtils.getOppositeColor(color)), ChessUtils.getOppositeColor(color)))
            {
                sum = CHECKMATE;
                IN_CHECKMATE = true;
                return sum;
            }
            if (MoveValidation.kingInCheck(board, MoveValidation.findKing(board, color), color))
            {
                sum = 5;
            }
            if (moves.Count == 0 && MoveValidation.kingInCheck(board, MoveValidation.findKing(board, color), color))
            {
               // sum = CHECKMATE;
                //IN_CHECKMATE = true;
                sum = -99999;
                return sum;
            }

            else if(moves.Count == 0)
            {
                sum = -99999;
                return sum;
            }
            for (int y = 0; y < ChessBoard.NumberOfRows; y++)
            {
                for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                {
                    ChessPiece currentPiece = board[x, y];
                    //StudentAI.studentAI.Log(board[x,y].ToString() + "\n");
                    if (MoveValidation.SameColor(currentPiece, color))
                    {
                        if (color == ChessColor.White && currentPiece == ChessPiece.WhitePawn && (y == 0 || y == 7))
                            sum += 8;
                        if (color == ChessColor.Black && currentPiece == ChessPiece.BlackPawn && (y == 0 || y == 7))
                            sum += 8;
                        sum += getPieceValue(currentPiece) * 10;
                    }
                    else //Opposite color = minus value 
                    {
                        if (color == ChessColor.White && currentPiece == ChessPiece.WhitePawn
                            && (y == 0 || y == 7))
                            sum -= 8;
                        if (color == ChessColor.Black && currentPiece == ChessPiece.BlackPawn
                            && (y == 0 || y == 7))
                            sum -= 8;
                        sum -= getPieceValue(currentPiece) * 10;
                    }

                }
            }
            //points for pawn promotion
            //if (currPiece == ChessPiece.WhitePawn)
            //    StudentAI.studentAI.Log("yo yo sloppy jo");
            Random r = new Random(Environment.TickCount);
            int lucky = r.Next()%7;
            if (MoveValidation.SameColor(currPiece, color) && (lucky == 1 || lucky == 2))
            {
                if (color == ChessColor.Black && currPiece == ChessPiece.BlackPawn
                          )
                    sum += 2;
                if (color == ChessColor.White && currPiece == ChessPiece.WhitePawn
                          )
                    sum += 2;
            }
            else
            {
                if (color == ChessColor.Black && currPiece == ChessPiece.BlackPawn
                          && lucky == 1 || lucky == 2)
                    sum += 1;
                if (color == ChessColor.White && currPiece == ChessPiece.WhitePawn
                          && lucky == 1 || lucky == 2)
                    sum += 1;
            }
            return sum;
        }//End basicSumScore


        /// <summary>
        /// Basic Sum Score with King movement restriction when enemy color has fewer pieces.
        /// </summary>
        /// <param name="board"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        public int sumScoreEnemyKingRestricted(ChessBoard board, ChessColor color, ChessMove move) {
            int mySum = 0;
            int enemySum = 0;
            int totalScore = 0;
            ChessLocation enemyKingLocation = MoveValidation.findKing(board, ChessUtils.getOppositeColor(color));
            List<ChessMove> moves = MoveGeneration.getAllMoves(board, ChessUtils.getOppositeColor(color));
            //moves.RemoveAll(move => MoveValidation.putsKingInCheck(board, move, ChessUtils.getOppositeColor(color)));
            if (moves.Count == 0)
            {
                if (MoveValidation.kingInCheck(board, enemyKingLocation, ChessUtils.getOppositeColor(color)))
                {
                    mySum = CHECKMATE;
                    IN_CHECKMATE = true;
                    return mySum;
                }
                else return -9999;
            }
            int xDiff, yDiff, manhattanDiff = 0;
            for (int y = 0; y < ChessBoard.NumberOfRows; y++)
            {
                for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                {
                    ChessPiece currentPiece = board[x, y];
                    if (MoveValidation.SameColor(currentPiece, color))
                    {
                        xDiff = Math.Abs(x - enemyKingLocation.X);
                        yDiff = Math.Abs(y - enemyKingLocation.Y);
                        if( xDiff > 1 && yDiff > 1)
                            manhattanDiff += (xDiff + yDiff);
                        mySum += getPieceValue(currentPiece);
                    }
                    else //Opposite color = minus value 
                    {
                        enemySum += getPieceValue(currentPiece);
                    }

                }
            }
            if (enemySum >= kingValue && enemySum <= kingValue + pawnValue * 6) {
                totalScore += 4 * (8 - MoveGeneration.generateKingMoves(board, MoveValidation.findKing(board, ChessUtils.getOppositeColor(color)), ChessUtils.getOppositeColor(color)).Count);
            }
            totalScore = totalScore + mySum - enemySum - manhattanDiff;

            return totalScore;
        }

        /// <summary>
        /// Basic Sum Score with King movement restriction when enemy color has fewer pieces.
        /// </summary>
        /// <param name="board"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        public int sumScoreBackupBonus(ChessBoard board, ChessColor color)
        {
            int mySum = 0;
            int enemySum = 0;
            int totalScore = 0;
            ChessLocation enemyKingLocation = MoveValidation.findKing(board, ChessUtils.getOppositeColor(color));
            List<ChessMove> moves = MoveGeneration.getAllMoves(board, ChessUtils.getOppositeColor(color));            
            if (moves.Count == 0)
            {
                if (MoveValidation.kingInCheck(board, enemyKingLocation, ChessUtils.getOppositeColor(color)))
                {
                    mySum = CHECKMATE;
                    IN_CHECKMATE = true;
                    return mySum;
                }
            }
            int xDiff, yDiff;
            List<ChessLocation> myPieces = new List<ChessLocation>();
            for (int y = 0; y < ChessBoard.NumberOfRows; y++)
            {
                for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                {
                    ChessPiece currentPiece = board[x, y];
                    if (MoveValidation.SameColor(currentPiece, color))
                    {
                        myPieces.Add(new ChessLocation(x, y));
                        xDiff = Math.Abs(x - enemyKingLocation.X);
                        yDiff = Math.Abs(y - enemyKingLocation.Y);
                        
                        mySum += getPieceValue(currentPiece);
                    }
                    else //Opposite color = minus value 
                    {
                        enemySum += getPieceValue(currentPiece);
                    }

                }
            }
            for (int i = 0; i < myPieces.Count; ++i) {
                for (int j = 0; j < myPieces.Count; ++j) {
                    //if (j == i)
                    //{
                    //    //Don't do compare same piece
                    //}
                    //else 
                    //{ 
                    //    if( MoveValidation
                    //}
                }
                //If this piece is backed up, add points
            }
            if (enemySum >= kingValue && enemySum <= kingValue + pawnValue * 6)
            {
                totalScore += 4 * (8 - MoveGeneration.generateKingMoves(board, MoveValidation.findKing(board, ChessUtils.getOppositeColor(color)), ChessUtils.getOppositeColor(color)).Count);
            }
            totalScore = totalScore + mySum - enemySum;

            return totalScore;
        }


        public int NumberOfPieces(ChessColor color, ChessBoard board)
        {
            int totalPieces = 0;
            for (int y = 0; y < ChessBoard.NumberOfRows; y++)
            {
                for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                {
                    ChessPiece currentPiece = board[x, y];
                    if (MoveValidation.SameColor(currentPiece, color))
                    {
                        totalPieces++;
                    }
                }
            }
            return totalPieces;
        }


        public ChessMove minimaxID(ChessBoard root, ChessColor myColor, HeuristicFunction utilityFunction, int startingDepth = 2)
       {
           //int outOfTime = -99999999;
           ChessMove bestMove = new ChessMove(null, null);
           bestMove.ValueOfMove = 0;
           ChessMove currentMove;
           for (int i = startingDepth; ; ++i)
           {
               StudentAI.studentAI.Log("iteration depth = " + i);
               currentMove = minimax(root, myColor, utilityFunction, i);
               if (currentMove.ValueOfMove <= -123456789)
                   break;
               bestMove = currentMove;                
           }
           return bestMove;
       }

        public ChessMove minimax(ChessBoard root, ChessColor myColor, HeuristicFunction utilityFunction, int maxDepth=5)
        {            
            //see how many pieces opponent has
            if(NumberOfPieces(ChessUtils.getOppositeColor(myColor),root) <= 4)
                maxDepth = 4;
            Stack<Node> stack = new Stack<Node>();
            Node currentNode = new Node(null, root, null, ChessUtils.getOppositeColor(myColor), 0);
            //Node bestMove = new Node(null, root, null, myColor, 0);
            ChessMove bestMove = currentNode.move;
            stack.Push(currentNode);
            //List<Node> bestMoves = new List<Node>();
            int iterations = 0;
            int bestScore = -99999;
            bool timeUp = false;
            //Int64 timeElapsed = StudentAI.stopwatch.ElapsedMilliseconds;
            //bool foundit = false;
            long timeAfterLimit = 0;
            StudentAI.stopwatch.Reset();
            StudentAI.stopwatch.Start();
            while (stack.Count != 0 )//&& timeElapsed <= 4800) // && !StudentAI.studentAI.IsMyTurnOver() )
            {
                if (StudentAI.stopwatch.ElapsedMilliseconds >= 4500 && timeUp == false)
                {
                    timeUp = true;
                    StudentAI.studentAI.Log("\n\nTIMES UP!!!\n\nElapsed Milliseconds = " + StudentAI.stopwatch.ElapsedMilliseconds);
                    timeAfterLimit = StudentAI.stopwatch.ElapsedMilliseconds;
                }
                iterations++;
                currentNode = stack.Peek();
                if (currentNode.depth == 0 && timeUp)
                    break;
                if (currentNode.depth >= maxDepth || timeUp)
                    currentNode.score = utilityFunction(currentNode.currBoard, myColor, currentNode.move);
                else if(!timeUp && currentNode.score == null)
                {
                    List<ChessMove> moves = MoveGeneration.getAllMoves(currentNode.currBoard, ChessUtils.getOppositeColor(currentNode.color));            
                    if (moves.Count == 0)
                    {
                        currentNode.score = utilityFunction(currentNode.currBoard, myColor, currentNode.move);
                    }
                    //get all children
                    
                    foreach(var move in moves)
                    {         
                        ChessBoard newBoard = currentNode.currBoard.Clone();
                        newBoard.MakeMove(move);
                        stack.Push(new Node(currentNode,newBoard,move,ChessUtils.getOppositeColor(currentNode.color),
                            currentNode.depth + 1));
                    }
                }
                if (currentNode.score != null)
                {
                    if (currentNode.depth == 0)
                    {
                        break; 
                    }
                    String tabs = "";
                    for (int i = currentNode.depth - 1; i > 0; i--)
                    {
                        tabs += "\t";
                    }
                    //if(currentNode.parent.score != null)
                    //StudentAI.studentAI.Log(currentNode.color + " From: X " + currentNode.move.From.X + " Y: " + currentNode.move.From.Y + " To: X " + currentNode.move.To.X + " Y: " + currentNode.move.To.Y + tabs + currentNode.depth + ": p_score " + currentNode.parent.score + " -new score " + currentNode.score);
                    if(currentNode.depth > 0 && currentNode.betterThanParentScore(currentNode.score))
                    {
                        currentNode.parent.score = currentNode.score;
                        //Alpha-Beta Pruning
                        //now that the parent is set,
                        //  check if the score is 'worse' than the parent
                        //  If it is worse,
                        if (currentNode.depth != 1 &&
                                currentNode.parent.score != currentNode.parent.parent.score &&
                                !currentNode.parent.betterThanParentScore(currentNode.parent.score))
                        {
                            //StudentAI.studentAI.Log("Pruned");
                            stack.Pop();
                            while (stack.Peek().score == null) stack.Pop();
                            stack.Pop();
                            continue;
                        }
                        else
                        {
                            //StudentAI.studentAI.Log("Not Pruned"); 
                        }
                    }
                    if (currentNode.depth == 1 && currentNode.score == bestScore)
                    {
                        Random r = new Random(Environment.TickCount);
                        if (r.Next(14) % 7 < 2)
                        {
                            StudentAI.studentAI.Log("RANDOM MOVE SELECTED");
                            bestMove = currentNode.move;
                            bestScore = (int)currentNode.score;
                        }
                    }
                    else if(currentNode.depth == 1 && currentNode.score >= bestScore)
                    {
                        //StudentAI.studentAI.Log(tabs + "old Best Move Score: " + bestScore + " new Best Move Score: " + currentNode.score);
                        bestMove = currentNode.move;
                        bestScore = (int)currentNode.score;
                    } 
                    stack.Pop();
                }
                //timeElapsed = StudentAI.stopwatch.ElapsedMilliseconds;
            }
            if (timeUp)
            {
                timeAfterLimit = StudentAI.stopwatch.ElapsedMilliseconds - timeAfterLimit;
                StudentAI.studentAI.Log("TIME TO EXIT: " + timeAfterLimit);
                bestMove.ValueOfMove = -123456789;
            }
            return bestMove;
        }
        
        public ChessMove greedy(ChessBoard board, ChessColor color, List<ChessMove> moves, HeuristicFunction function) {
            ChessMove best = null;
            int bestValue = int.MinValue;
            ChessBoard newBoard;
            foreach (ChessMove move in moves) {
                if (best == null)
                {
                    best = move;
                }
                else 
                {
                    newBoard = new ChessBoard(board.ToPartialFenBoard());
                    newBoard.MakeMove(move);
                    if (function(newBoard, color, null) > bestValue) {
                        best = move;
                    }
                }
            }
            return best;
        }

        public ChessMove greedyRandomized(ChessBoard board, ChessColor color, List<ChessMove> moves, HeuristicFunction function)
        {
            List<ChessMove> best = new List<ChessMove>();
            int bestValue = int.MinValue;
            ChessBoard newBoard;
            foreach (ChessMove move in moves)
            {
                if (best.Count == 0)
                {
                    best.Add(move);
                }
                else
                {
                    newBoard = new ChessBoard(board.ToPartialFenBoard());
                    newBoard.MakeMove(move);
                    int currentValue = function(newBoard, color, null);
                    if (currentValue == CHECKMATE)
                        return move;
                    if ( currentValue > bestValue)
                    {
                        bestValue = currentValue;
                        best.Clear();
                        best.Add(move);
                    }
                    else if (currentValue == bestValue) {                        
                        best.Add(move);
                    }
                }
            }            
            Random r = new Random(Environment.TickCount);
            if (best.Count == 0)
                return null;
            return best[r.Next()%best.Count];
        }

    }//End Class Heuristics
}
